

use crate::probe::DebugProbeInfo;


/// Finds all CMSIS-DAP devices, either v1 (HID) or v2 (WinUSB Bulk).
///
/// This method uses nusb to read device strings, which might fail due
/// to permission or driver errors, so it falls back to listing only
/// HID devices if it does not find any suitable devices.
#[tracing::instrument(skip_all)]
pub fn list_cmsisdap_devices() -> Vec<DebugProbeInfo> {
    Vec::new()
}
/// unix 
#[cfg(unix)]
pub mod unix {
    use crate::probe::{cmsisdap::{commands::CmsisDapDevice, CmsisDap}, Probe};
    use anyhow::{anyhow, Result};
    use rusb::{Direction, EndpointDescriptor, GlobalContext, TransferType, UsbContext};
    use std::time::Duration;

    fn open_cmsisdap(fd: i32) -> Result<CmsisDapDevice> {
        
        let ctx = GlobalContext {};
        ctx.as_raw();
        let handle = unsafe {
            ctx.open_device_with_fd(fd)?
        };
    
        let device = handle.device();
        let timeout = Duration::from_secs(1);
        let lans = handle.read_languages(timeout)?;
        if lans.len() < 1 {
            return Err(anyhow!("lans < 1"));
        }
        let lan = lans[0];
        let desc = device.device_descriptor()?;
        
        for conf_index in 0..desc.num_configurations() {
            let conf = device.config_descriptor(conf_index)?;
            for inter in conf.interfaces() {
                for inter_desc in inter.descriptors() {
                    
                    log::debug!("hid:{}", inter_desc.class_code());
    
                    let inter_str = handle.read_interface_string(lan, &inter_desc, timeout)?;
                    
                    if !is_cmsis_dap(&inter_str) {
                        log::debug!("not dap:{}", inter_str);
                        continue;
                    }
    
                    let ep_count = inter_desc.num_endpoints();
                    if (ep_count != 2) && (ep_count != 3) {
                        log::debug!("ep:{}", ep_count);
                        continue;
                    }
    
                    let eps: Vec<EndpointDescriptor> = inter_desc.endpoint_descriptors().collect();
    
                    if eps[0].transfer_type() != TransferType::Bulk || eps[0].direction() != Direction::Out {
                        log::debug!("ep0 invalid");
                        continue;
                    }
            
                    if eps[1].transfer_type() != TransferType::Bulk || eps[1].direction()!= Direction::In {
                        log::debug!("ep1 invalid");
                        continue;
                    }
     
                    let mut swo_ep = None;
            
                    if eps.len() > 2
                        && eps[2].transfer_type() == TransferType::Bulk
                        && eps[2].direction() == Direction::In
                    {
                        swo_ep = Some((eps[2].address(), eps[2].max_packet_size() as usize));
                    }
                    log::debug!("select:{} {}", inter_desc.class_code(), inter_str);
                    handle.claim_interface(inter_desc.interface_number())?;
                    let out_ep = eps[0].address();
                    let in_ep = eps[1].address();
                    let max_packet_size = eps[1].max_packet_size() as usize;
                    let cdd = CmsisDapDevice::new(handle, out_ep, in_ep, max_packet_size, swo_ep);
                    return Ok(cdd);
                }
            }
        }
    
        Err(anyhow!("no find cmsis dap"))
    }
    
    /// open_by_fd
    pub fn open_by_fd(fd: i32) -> Result<Probe> {
    
        let dev = open_cmsisdap(fd)?;
        let dap = CmsisDap::new_from_device(dev)?;
        let probe = Probe::new(dap);
    
        Ok(probe)
    }
    
    /// open_by_fd_ep
    pub fn open_by_fd_ep(fd: i32, out_ep: u8, in_ep: u8, max_packet_size: usize) -> Result<Probe> {
        
        let handle = unsafe {
            let ctx = GlobalContext{};
            ctx.as_raw();
            ctx.open_device_with_fd(fd)?
        };
        let cdd = CmsisDapDevice::new(handle, out_ep, in_ep, max_packet_size, None);
        let dap = CmsisDap::new_from_device(cdd)?;
        Ok(Probe::new(dap))
    }
    
    /// We recognise cmsis dap interfaces if they have string like "CMSIS-DAP"
    /// in them. As devices spell CMSIS DAP differently we go through known
    /// spellings/patterns looking for a match
    fn is_cmsis_dap(id: &str) -> bool {
        id.contains("CMSIS-DAP") || id.contains("CMSIS_DAP")
    }
    

}

